<?php
declare(strict_types = 1);
/**
 * 常用的校验工具类
 *
 * User: Shanla
 * Date: 2021/1/9
 * Time: 5:05 PM
 */

namespace tool;


class CheckTool
{
    /**
     * 校验手机号格式
     *
     * @author Shanla
     * @param string $mobile
     * @return bool
     */
    public static function checkMobile(string $mobile) : bool
    {
        if (preg_match('/^1[3|4|5|6|7|8|9]\d{9}$/', $mobile)) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 校验邮箱格式
     *
     * @author Shanla
     * @param string $email
     * @return bool
     */
    public static function checkEmail(string $email) : bool 
    {
        if (preg_match('/^(\w)+(\.\w+)*@(\w)+((\.\w+)+)$/', $email)) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 校验生日格式
     *
     * @author Shanla
     * @param string $birth
     * @param string $format
     * @return bool
     */
    public static function checkBirth(string $birth, string $format = 'Y-m-d') : bool
    {
        $unixTime = strtotime($birth);
        if (!$unixTime) {
            return false;
        }

        // 校验生日的格式是否正确
        if (date($format, $unixTime) == $birth) {
            return true;
        } else {
            return false;
        }
    }
	
	/**
	 * 身份证校验
	 *
	 * @author Shanla
	 * @param string $idCard 身份证号
	 * @param int $type 身份证类型 1-大陆身份证号 2-香港身份证 3-澳门身份证 4-台湾身份证
	 * @return boll
	 */
	public static function checkIdCard(string $idCard, int $type = 1) : bool
	{
		if (1 == $type)	{
			// 大陆身份证
			// 大陆身份证城市码检验
			$vCity = array(
			   	'11', '12', '13', '14', '15', '21', '22',
				'23', '31', '32', '33', '34', '35', '36',
				'37', '41', '42', '43', '44', '45', '46',
				'50', '51', '52', '53', '54', '61', '62',
				'63', '64', '65', '71', '81', '82', '91'
			);
			if (!preg_match('/^([\d]{17}[xX\d]|[\d]{15})$/', $idCard)) {
			   	return false;
			}

			if (!in_array(substr($idCard, 0, 2), $vCity)) {
				return false;
			}

			$idCard = preg_replace('/[xX]$/i', 'a', $idCard);
			$vLength = strlen($idCard);

			if ($vLength == 18) {
				$vBirthday = substr($idCard, 6, 4) . '-' . substr($idCard, 10, 2) . '-' . substr($idCard, 12, 2);
			} else {
				$vBirthday = '19' . substr($idCard, 6, 2) . '-' . substr($idCard, 8, 2) . '-' . substr($idCard, 10, 2);
			}

			if (date('Y-m-d', strtotime($vBirthday)) != $vBirthday) {
				return false;
			}
			if ($vLength == 18) {
				$vSum = 0;

				for ($i = 17; $i >= 0; $i--) {
					$vSubStr = substr($idCard, 17 - $i, 1);
					$vSum += (pow(2, $i) % 11) * (($vSubStr == 'a') ? 10 : intval($vSubStr, 11));
				}

				if ($vSum % 11 != 1) {
					return false;
				}
			}
		} elseif (2 == $type) {
			// 香港身份证
			// 香港身份证号码由三部分组成：一个英文字母；6个数字；括号及0-9中的任一个数字，或者字母A。括号中的数字或字母A，是校验码，用于检验括号前面的号码的逻辑正确性。
			// 逻辑关系: 先把首位字母改为数字，即A为1，B为2，C为3...Z为26，再乘以8；然后把字母后面的6个数字依次乘以7、6、5、4、3、2；再将以上所有乘积相加的和，除以11，得到余数；如果整除，则括号中的校验码为0，如果余数为1，则校验码为A，如果余数为2～10，则用11减去这个余数的差作校验码。例如：P103265（1），P，在字母表中排行16，则以16代表，则计算校验码：16×8＋1×7＋0×6＋3×5＋2×4＋6×3＋5×2＝186 186÷11＝16......余10 11－10＝1，即校验码为1。
			if (strlen($idCard) != 10) {
				return false;
			}
			if (!preg_match('/[A-Z][0-9]{6}\([0-9A]\)/', $idCard) && !preg_match('/[A-Z][0-9]{6}（[0-9A]）/')) {
				return false;
			}
			// 逻辑规则检验
			$charArray = [
				'A' => 1,
				'B' => 2,
				'C' => 3,
				'D' => 4,
				'E' => 5,
				'F' => 6,
				'G' => 7,
				'H' => 8,
				'I' => 9,
				'J' => 10,
				'K' => 11,
				'L' => 12,
				'M' => 13,
				'N' => 14,
				'O' => 15,
				'P' => 16,
				'Q' => 17,
				'R' => 18,
				'S' => 19,
				'T' => 20,
				'U' => 21,
				'V' => 22,
				'W' => 23,
				'X' => 24,
				'Y' => 25,
				'Z' => 26,
			];
			$sumNum = 0;
			for ($i=0; $i<strlen($idCard); $i++) {
				if (0 == $i) {
					$sumNum += $charArray[$idCard[$i]] * 8;
				}
				if ($i > 0 && $i < 7) {
					$sumNum += $idCard[$i] * (8-$i);	
				}
				if ($i == strlen($idCard) - 2) {
					$lastNum = $idCard[$i];
				}
			}
			$verifyNum = $sumNum % 11;
			if (0 == $verifyNum) {
				$verfiy = 0;
			} else if (1 == $verifyNum) {
				$verify = 'A';
			} else {
				$verify = 11 - $verifyNum; 
			}
			if (!isset($lastNum) || $lastNum != $verify) {
				return false;
			}
		} elseif (3 == $type) {
			// 澳门身份证
			// 澳门身份证号码由8个拉丁数字组成，格式为：XNNNNNN(Y)。其中：第一位X，可能是1、5、7。绝大多数人以1字开首；以5字开首的身份证号码代表持有或曾经持有葡萄牙国民身份证或葡萄牙给外国人身份证之人士；以7字开首代表曾经取得蓝卡之人士，大多都是在1970年代至1980年代期间从中国大陆持合法证件到澳门的人士。最后一位Y，是查核用数字，是为方便电脑处理资料及检查号码输入的正确性而设。中间6位数字，是发证当局给出的顺序号。
			if (strlen($idCard) != 10) {
				return false;
			}
			if (!preg_match('/[157][0-9]{6}([0-9])/', $idCard)) {
				return false;
			}
		} else {
			// 台湾身份证
			// 中国台湾地区的身份证称为“国民身份证”，号码一共有10位，第1位是大写的英文字母，后9位是阿拉伯数字。比如：U193683453。第一位大写的英文字母是地区编码，代表初次登记的户籍所在地，比如，U代码花莲县。每个地区编码还对应有一个两位数的验证码，用于公式验证。台湾的所有地区编码和验证码如下：

			// 英文 县市 / 地区 验证码（数字）
			/* A 台北市 10
			   B 台中市 11
			   C 基隆市 12
			   D 台南市 13
			   E 高雄市 14
			   F 台北县 15
			   G 宜兰县 16
			   H 桃园县 17
			   I 嘉义市 34
			   J 新竹县 18
			   K 苗栗县 19
			   L 台中县 20
			   M 南投县 21
			   N 彰化县 22
			   O 新竹市 35
			   P 云林县 23
			   Q 嘉义县 24
			   R 台南县 25
			   S 高雄县 26
			   T 屏东县 27
			   U 花莲县 28
			   V 台东县 29
			   W 金门县 32
			   X 澎湖县 30
			   Y 阳明山管理局 31
			   Z 连江县 33
			 */
			// 首位数字代表性别，男性为1、女性为2；最后一位数字是检验码；其它是电脑系统给码。
			// 最后一位数字是检验码，通过公式计算得到。计算公式如下：
			// 通算值= 首字母对应的第一位验证码+ 首字母对应的第二位验证码 * 9 + 性别码 * 8 + 第二位数字 * 7 + 第三位数字 * 6 + 第四位数字 * 5 + 第五位数字 * 4 + 第六位数字 * 3 + 第七位数字 * 2 + 第八位数字 * 1
			// 最后一位数 =10- 通算值的末尾数。
			// 例如，A234567893，A对应的验证码是10，最后一位数是3。通算值= 1 + 0*9 + 2*8 + 3*7 + 4*6 + 5*5 + 6*4 + 7*3 + 8*2 + 9*1 = 157，通算值的末尾数是7。则10-7=3，与最后一位数（验证码）相同，身份证号码正确。反之，A234567890的最后一位是0，就不是有效字号。
			if (strlen($idCard) != 10) {
				return false;
			}
			if (!preg_match('/[A-Z][0-9]{9}/', $idCard)) {
				return false;
			}
			// 身份证号逻辑检验
			$charArray = [
				'A' => [1, 0],
				'B' => [1, 1],
				'C' => [1, 2],
				'D' => [1, 3],
				'E' => [1, 4],
				'F' => [1, 5],
				'G' => [1, 6],
				'H' => [1, 7],
				'I' => [3, 4],
				'J' => [1, 8],
				'K' => [1, 9],
				'L' => [2, 0],
				'M' => [2, 1],
				'N' => [2, 2],
				'O' => [3, 5],
				'P' => [2, 3],
				'Q' => [2, 4],
				'R' => [2, 5],
				'S' => [2, 6],
				'T' => [2, 7],
				'U' => [2, 8],
				'V' => [2, 9],
				'W' => [3, 2],
				'X' => [3, 0],
				'Y' => [3, 1],
				'Z' => [3, 3],
			];
			$idCardLen = strlen($idCard);
			$sumNum = 0;
			for ($i = 0; $i < $idCardLen; $i++) {
				if (0 == $i) {
					$sumNum += $charArray[$idCard[$i]][0] + $charArray[$idCard[$i]][1] * 9;
				}
				if ($i > 0 && $i < $idCardLen - 1) {
					$sumNum += $idCard[$i] * (9 - $i);
				}
				if ($i == $idCardLen - 1) {
					$lastNum = $idCard[$i];
				}
			}
			$sumNum = (string)$sumNum;
			$verify = $sumNum[strlen($sumNum) - 1] ?? -1;
			if (!isset($lastNum) || $verify == -1 ||  $lastNum != 10 - $verify) {
				return false;
			}
		}

		return true;
	}

	/**
	 * 检查年月格式
	 *
	 * @author Shanla
	 * @param $month
	 * @return bool
	 */
	public static function checkMonth(string $month) : bool
	{
		return preg_match("/^[1-9][0-9]{3}[-](0?[1-9]|1[012])$/", $month);
	}

	/**
	 * 校验微信号格式
	 *
	 * @author Shanla
	 * @param string $weChat
	 * @return bool
	 */
	public static function checkWeChat(string $weChat) : bool
	{
		return preg_match("/^[a-zA-Z\d_-]{5,}$/", $weChat);
	}

	/**
	 * 检验json串是否合法（PHP version > 5.3）
	 *
	 * @author Shanla
	 * @param string $jsonString
	 * @return bool;
	 */
	public static function checkJson(string $jsonString) : bool
	{
		json_decode($jsonString);

		return json_last_error() == JSON_ERROR_NONE;
	}

	/**
	 * 校验图片链接格式
	 *
	 * @author Shanla
	 * @param string $imageUrl
	 * @return bool
	 */
	public static function checkImageUrl(string $imageUrl) : bool
	{
		return preg_match("/(\w+)[\w\W\/\.\-]*(jpg|jpeg|gif|png)/", $imageUrl);
	}


}

















